#!/usr/bin/perl -w
our $Version;
use strict;

#    Pete's Automated VGAP Hosting Thingy
#    Copyright (C) 1997-2003 Peter Harris
$    Version = '0.1.4';
#
#    This program is free software; you can redistribute it and/or modify
#    it under the terms of the GNU General Public License version 2 as
#    published by the Free Software Foundation.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program; if not, write to the Free Software
#    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
#    This program is currently hosted at http://www.agamemnon.net/vgap/

use Net::IMAP::Simple;
use MIME::Parser;
use MIME::Base64;
use Net::SMTP;
use IniConf;
$main::TZ='EST5EDT';
use Date::Manip;
use Digest::SHA1 'sha1_base64';

config MIME::ToolUtils DEBUGGING => 0;

# needed
our $ininame;
our $mainini;
our ($NumberOfGames,@ini);
our %UnShortName;
our ($maininitime, @initime);
our $running;

# wanted for optimization reasons: These are used every $MainDelay seconds.
our ($DebugLog,$DebugCon,$LogFileName);
our ($POPhost, $POPuser, $POPpw);
our $MainDelay;

sub GetSalt {
   my $game = shift;
   my $salt = $ini[$game]->val('General','Salt');
   if (!defined($salt)) {
      $salt = '';
      $salt .= ('.', '/', 0..9, 'A'..'Z', 'a'..'z')[rand 64] foreach(1..8);
      $ini[$game]->setval('General','Salt',$salt);
      UpdateConfig($game);
   }
   return $salt;
}

sub GenerateCookie {
   my $game = shift;
   my $type = shift;
   my $email = shift;
   my $salt = GetSalt($game);

   return sha1_base64($salt, $type, $email);

   # A crypt based implementation would probably want to
   # use <the first letter of $type>.$email , $salt
   # And then trim the first two letters (the salt) from
   # the front of the digest before returning it.
}

sub EnvelopeTo {
   my $to = shift;
   my $subject = shift;

   my $smtp = Net::SMTP->new($mainini->val('EMail','SMTP'));
   $smtp->mail($mainini->val('EMail','SMTPUser'));
   $smtp->to($to);
   $smtp->data();

   my $from = $mainini->val('General','VGAPEmail');
   $smtp->datasend("From: Pete\'s Automated VGAP Thingy <$from>\n");
   $smtp->datasend("To: $to\n");
   $smtp->datasend("Subject: [VGA Planets] $subject\n");
   $smtp->datasend("\n");

   return $smtp;
}

sub RaceNames {
   my @races = undef;
   my $a = shift;
   if (open FILE, $ini[$a]->val('General','Homedir').'race.nm' or
       open FILE, $ini[$a]->val('General','VGAPdir').'race.nm') {
      my $glob;
      $glob = <FILE>;
      close FILE;
      @races=unpack 'A30'x 11, $glob;
   }
   return @races;
}

sub invite {
   my $to = shift;
   my $game = shift;

   logit(4,"Inviting $to\n");

   my $shortName = $ini[$game]->val('General','ShortName');
   my $longName = $ini[$game]->val('General','LongName');
   my $smtp = EnvelopeTo($to, 'Invitation Message');
   $smtp->datasend("Congratulations! You have been invited to join a VGA Planets game!\n");
   $smtp->datasend("The game is called $longName.\n");
   $smtp->datasend("Use the short name '$shortName' in commands.\n\n");
   $smtp->datasend("This message is being sent to you by an automated VGA Planets system\n If you have never used the system before, plese send a message with\nthe subject 'VGAP' and the word 'help' on a line by itself in the body\n to the sender of this message. (Just hit 'reply')\n\n");

   if (uc($ini[$game]->val('General','Status')) eq 'INVITATION ONLY') {
      $smtp->datasend("This is an invitation-only game. You must include the following\nline in your message before your 'join' command:\n\n");
      $smtp->datasend('cookie '.GenerateCookie($game,'invite',$to)."\n");
   }

   $smtp->dataend();
   $smtp->quit;   
}

sub resign {
   my $player = shift;
   my $game = shift;
   my $resigncookie = GenerateCookie($game, 'resign', $player);
   my $to = $ini[$game]->val("Player$player", 'Email');
   my $smtp;

   if (!defined($main::cookie) || ($main::cookie ne $resigncookie) ) {
      logit (3," Sending resign cookie to player $player of game $game.\n");
      $smtp = EnvelopeTo($to, 'So you want to quit, eh?');

      $smtp->datasend("Resign command detected.\n\n");
      $smtp->datasend("So you want to quit, eh? Well, just to be sure, you have\n");
      $smtp->datasend("to send the resign command agin. This time, however, you\n");
      $smtp->datasend("must include the following line in your message before 'resign',\n");
      $smtp->datasend("in addition to your usual 'auth' command:\n\n");
      $smtp->datasend("cookie $resigncookie\n");
      $smtp->datasend("\n\nBe very, very certain you want to quit before doing this.\n");
      $smtp->datasend("Resigning is permanent and irreversable. There is no 'unresign' command.\n");
      $smtp->datasend("\n\nIf you sent the resign command by accident, simply\n");
      $smtp->datasend("do nothing. Your friendly neighborhood saftey net has caught\n");
      $smtp->datasend("the mistake for you. You're welcome.\n");

      $smtp->dataend();
      $smtp->quit;
      return;
   }
   logit (3," Resign cookie of player $player of game $game is valid.\n");

   $smtp = EnvelopeTo($to, "Goodbye, player $player of ".$ini[$game]->val('General','ShortName'));
   $smtp->datasend("Resign command sucessful.\n\n");
   $smtp->datasend("You have been removed from this game.\n");
   $smtp->dataend();
   $smtp->quit;

   $ini[$game]->setval("Player$player",'Active','NO');
   UpdateConfig($game);
}

sub killrace {
   my $player = shift;
   my $game = shift;
   my $killcookie = GenerateCookie($game, 'kill', $player);
   my $to = $ini[$game]->val('Admin', 'Email');
   my @races = RaceNames($game);
   my $smtp;

   if ( (!defined $main::cookie) || ($main::cookie ne $killcookie) ) {
      logit (3," Sending kill cookie for player $player to admin of game $game.\n");
      $smtp = EnvelopeTo($to, 'So you want to kill, eh?');

      $smtp->datasend("Kill command detected.\n\n");
      $smtp->datasend("So you want to kill $races[$player-1], eh?\n");
      $smtp->datasend("Well, just to be sure, you have\n");
      $smtp->datasend("to send the kill command agin. This time, however, you\n");
      $smtp->datasend("must include the following line in your message before 'kill foo':\n\n");
      $smtp->datasend("cookie $killcookie\n");
      $smtp->datasend("\n\nBe very, very certain you want to kill before doing this.\n");
      $smtp->datasend("Killing is permanent and irreversable. There is no 'unkill' command.\n");
      $smtp->datasend("\n\nIf you sent the kill command by accident, simply\n");
      $smtp->datasend("do nothing. Your friendly neighborhood saftey net has caught\n");
      $smtp->datasend("the mistake for you. You're welcome.\n");

      $smtp->dataend();
      $smtp->quit;
      return;
   }
   logit (3," Kill cookie of the admin for player $player of game $game is valid.\n");

   $smtp = EnvelopeTo($to, "Player $player of ".$ini[$game]->val('General','ShortName')." is dead, Jim.");
   $smtp->datasend("Kill command sucessful.\n\n");
   $smtp->datasend("$races[$player-1] has been removed from this game.\n");
   $smtp->dataend();
   $smtp->quit;

   $to = $ini[$game]->val("Player$player", 'Email');
   $smtp = EnvelopeTo($to, "Goodbye, player $player of ".$ini[$game]->val('General','ShortName'));
   $smtp->datasend("The administrator of this game has seen fit to remove you from\n");
   $smtp->datasend("the game. You are no longer playing in ".$ini[$game]->val('General','ShortName')."\n");
   $smtp->datasend("\nGoodbye. So long. Farewell. This is your last and final message.\n");
   $smtp->dataend();
   $smtp->quit;

   $ini[$game]->setval("Player$player",'Active','NO');
   UpdateConfig($game);
}

sub helpto { # add warnings et al here.
   my $to = shift;

   logit(4,"Helping $to\n");

   my $smtp = EnvelopeTo($to, 'Help Message');

   $smtp->datasend("No valid commands found in your message.\n\n") if (shift);
   $smtp->datasend("This is Pete's Automated VGAP Thingy, Version $Version\n\n");
   $smtp->datasend("The thingy will only process email if one of 'VGAP' or 'VGA Planets'\n is in the subject line somewhere.\n\n");
   $smtp->datasend("Commands currently supported by the VGAP thingy:\n");
   $smtp->datasend("   admin    - mails your message to the VGAP thingy's administrator.\n");
   $smtp->datasend("              implies end. See end.\n");
   $smtp->datasend("            - If your message has anything to do with a game in progress,\n");
   $smtp->datasend("              please issue a valid auth command first.\n");
   $smtp->datasend("   auth     - authorization request. Required before any commands\n");
   $smtp->datasend("              such as comp, email, pass, submit, resend, or quit.\n");
   $smtp->datasend("            - uses the form GAME:RACE#:PASSWORD\n");
   $smtp->datasend("              note that (unlike most other things) games and passwords are\n");
   $smtp->datasend("              case-sensitive.\n");
   $smtp->datasend("   check    - like desc, only it sends you a list of who has/has not\n");
   $smtp->datasend("              submitted their turn file.\n");
   $smtp->datasend("   comp     - turns on or off the computer player for your race.\n");
   $smtp->datasend("              Use this if you are going to be away for a short while and\n");
   $smtp->datasend("              you want the computer to play your turns for you.\n");
   $smtp->datasend("            - The computer will only play your turn if you haven't\n");
   $smtp->datasend("              submitted a valid .TRN file already.\n");
   $smtp->datasend("            - Setting 'comp on' will disable warnings, but will not\n");
   $smtp->datasend("              disable .RST mailings.\n");
   $smtp->datasend("            - e.g.:   comp on\n");
   $smtp->datasend("                      comp off\n");
   $smtp->datasend("   desc     - mails you a description of a game. eg: desc game1\n");
   $smtp->datasend("   email    - changes the e-mail address that the VGAP thingy sends your\n");
   $smtp->datasend("              .RST files to. eg. 'email foobar\@hotmail.com'.\n");
   $smtp->datasend("   end      - end of commands. Useful if you have a signature that\n");
   $smtp->datasend("              contains words that might be mistaken for commands.\n");
   $smtp->datasend("   help     - mails this list to you.\n");
   $smtp->datasend("   join     - request to join a game. Include a password, followed by,\n");
   $smtp->datasend("              in order of preference, a list of races you might\n");
   $smtp->datasend("              want to play.\n");
   $smtp->datasend("              eg.  join GAME1:password:2:7:1  means you want to join GAME1,\n");
   $smtp->datasend("              as the Lizards. Or the Crystals. Or the Federation. In that order.\n");
   $smtp->datasend("   list     - mails you a list of all currently active games.\n");
   $smtp->datasend("   pass     - changes the password that you use for the auth command.\n");
   $smtp->datasend("              Requires a successful auth first.\n");
   $smtp->datasend("   resend   - mails you a copy of your .RST file. Mostly only useful\n");
   $smtp->datasend("              if you manage to lose your original copy.\n");
   $smtp->datasend("   resign   - Quit the auth'd game. Permanent and irreversable.\n");
   $smtp->datasend("   submit   - indicates to the VGAP thingy that your .TRN file is attached to\n");
   $smtp->datasend("              this e-mail. If you submit more than once in a given turn, only\n");
   $smtp->datasend("              most recently submitted turn file will be used.\n");
   $smtp->datasend("              NOTE: You MUST send a vaild auth command BEFORE you send a submit.\n");
   $smtp->datasend("   unsubmit - You made a mistake. You're fixing it. You don't want the\n");
   $smtp->datasend("              turn to run just yet. This command undoes your submit.\n");
   $smtp->datasend("\n Note that each command must appear on a line by itself.\n");

   $smtp->dataend();
   $smtp->quit;
}

sub logit {
   my $level=shift;
   my $now = ParseDate('today');

   if ($level <= $DebugLog) {
      open FILE,">>$LogFileName" or print "Can't open LogFile $LogFileName because $!\n";
      print FILE "[$now] ",@_;
      close FILE;
   }

   if ($level <= $DebugCon) {
      print "[$now] ",@_;
   }
}

sub listto {
   my ($a,$b);
   my $to = shift;

   logit(4,"$to is asking for a list\n");

   my $smtp = EnvelopeTo($to, 'List of Active Games');

   $b = sprintf("%12s%40s%12s\n\n",'Name','Long Name','Status');
   $smtp->datasend($b);

   for ($a=1; $a<=$NumberOfGames; $a++) {
      $b = sprintf("%12s%40s%12s\n",
         $ini[$a]->val('General','ShortName'),
         $ini[$a]->val('General','LongName'),
         $ini[$a]->val('General','Status') );
      $smtp->datasend($b);
   }

   $smtp->dataend();
   $smtp->quit;
}

sub descto {
   my $to = shift;
   my $a = shift;
   my $auth = shift;
   my $authgame = shift;
   my ($b,@b);

   return if (!defined $a);

   $b =$ini[$a]->val('General','ShortName');
   my $smtp = EnvelopeTo($to, "Description of Game $b");

   logit(4,"$to is asking for a desc of $b\n");

   @b=$ini[$a]->val('General','Description');
   $smtp->datasend(shift(@b)."\n") while (@b);
   my @races = RaceNames($a);
   if (uc($ini[$a]->val('General','Status')) eq 'OPEN' ||
       uc($ini[$a]->val('General','Status')) eq 'INVITATION ONLY') {
      $smtp->datasend("\n Races still available:");
      for $b (1..11) {
         $smtp->datasend("\n $b - $races[$b-1]") if ( uc($ini[$a]->val("Player$b",'Active')) eq 'NO');
      }
      $smtp->datasend("\n\n Races already taken:");
   } else {
      $smtp->datasend("\n Races in play:");
   }
   my $showPlayers = 0;
   $showPlayers = 1 if (uc($ini[$a]->val('General','HiddenPlayers')) eq 'NO');
   $showPlayers = 1 if (defined($auth) && $auth == -1);   # SysAdmin
   $showPlayers = 1 if (defined($auth) && $auth == 0 && $authgame == $a); # Admin of this game

   for $b (1..11) {
      if (uc($ini[$a]->val("Player$b",'Active')) ne 'NO') {
         if ($showPlayers) {
            my $player = $ini[$a]->val("Player$b",'Email');
            $player =~ s/@.*$//;
            $smtp->datasend("\n $b - $races[$b-1] <$player>");
         } else {
            $smtp->datasend("\n $b - $races[$b-1]");
         }
      }
   }

   $smtp->datasend("\n");
   $smtp->dataend();
   $smtp->quit;
}

sub TurnNumber {
   my $game = shift;
   my $none = $ini[$game]->val('General','Homedir') . "nextturn.hst";

   open(FILE,$none) or return 0;
   binmode FILE;
   sysread FILE,$none,20,0;
   close FILE;
   return unpack('x18S',$none);
}

sub checkbody {
   my $smtp = shift;
   my $a = shift;
   my $status = $ini[$a]->val('General','Status');

   if (uc($status) eq 'OPEN' || uc($status) eq 'INVITATION ONLY') {
      $smtp->datasend("\n Sorry, this game has not started yet.\n");
   } else {
      my @races;
      my $none;
      my $b;
      my $turnnumber = TurnNumber($a);
   
      @races = RaceNames($a);
      $smtp->datasend("\nWe are currently on turn $turnnumber.\n");
      $smtp->datasend("Races that have kindly submitted a .TRN file:\n");
      $none=1;
      for ($b=1; $b<=11; $b++) {
         my $fn;

         if (uc($ini[$a]->val("Player$b",'Active')) eq 'YES') {
            $fn = $ini[$a]->val('General','Homedir');
            $fn .= "player$b.trn";
            $smtp->datasend("    $races[$b-1]\n"), $none=0 if (-e $fn);
         }
      }
      $none and $smtp->datasend("    None.\n");
      $smtp->datasend("\nRaces that have not yet submitted a .TRN file:\n");
      $none=1;
      for ($b=1; $b<=11; $b++) {
         my $fn;

         if (uc($ini[$a]->val("Player$b",'Active')) eq 'YES') {
            $fn = $ini[$a]->val('General','Homedir');
            $fn .= "player$b.trn";
            $smtp->datasend("    $races[$b-1]\n"), $none=0 if (!(-e $fn));
         }
      }
      $none and $smtp->datasend("    None.\n");
      $none=1;
      for ($b=1; $b<=11; $b++) {
         my $fn;

         if (uc($ini[$a]->val("Player$b",'Active')) eq 'COMP') {
            $fn = $ini[$a]->val('General','Homedir');
            $fn .= "player$b.trn";
            if (!(-e $fn)) {
               $none and $smtp->datasend("\nComputer played races:\n");
               $none=0;
               $smtp->datasend("    $races[$b-1]\n");
            }
         }
      }

      $smtp->datasend("\n  The host will");
      $smtp->datasend(" not") if ((uc($status) eq 'PAUSED') or ( uc($ini[$a]->val('Running','RunEarly')) ne 'YES') );
      $smtp->datasend(" run immediately upon reciept of\nany remaining turns.\n");
      if (uc($status) eq 'RUNNING') {
         my $date = UnixDate(ParseDate($ini[$a]->val('NextRun','NextRun')),"on %A, %B the %E at %i:%M%p");
         $smtp->datasend("\n  The host is scheduled to run in any case\n$date\n");
      } elsif (uc($status) eq 'PAUSED') {
         $smtp->datasend("\n  This game is currently Paused\n");
      }
   }
}

sub checkto {
   my $to = shift;
   my $game = shift;
   my $a = $UnShortName{lc($game)};
   my $b;
   my $smtp;
   my $date;

   logit(5,'Checkto:','"',$to,'"',"\n");
   if (!defined $a) {
      $smtp = EnvelopeTo($to, "Failed check on Game $game");
      logit(4,"$to can't check $game\n");
      $smtp->datasend("\n Sorry, the game $game does not exist.\n");
   } else {
      $b =$ini[$a]->val('General','ShortName');
      $smtp = EnvelopeTo($to, "Check on Game $b");

      logit(4,"$to is checking $b\n");

      checkbody($smtp, $a);
   }
   $smtp->dataend();
   $smtp->quit;
}

sub authinvalidto {
   my $to = shift;
   my $ia = shift;

   my $smtp = EnvelopeTo($to, 'Authorization invalid');

   $smtp->datasend("Authorization invalid.\nInvalid authorization attempt:\n\n$ia");

   $smtp->dataend();
   $smtp->quit;
}

sub invalidto {
   my $to = shift;
   my $ia = shift;
   my $smtp = EnvelopeTo($to, 'Invalid Command');

   $smtp->datasend("Invalid Command.\nThe following command is invalid, and has been ignored:\n\n$ia");

   $smtp->dataend();
   $smtp->quit;
}

sub UpdateConfig {
   my $game = shift;
   my $gameini = $mainini->val('GameINI',"Game$game");

   $ini[$game]->WriteConfig($gameini.'.new.ini');
   unlink($gameini);
   rename($gameini.'.new.ini',$gameini);
   $initime[$game] = (stat($gameini))[9];
}

sub joingame {
   my $to = shift;
   my $jc = shift;
   my $ecopy;
   my $smtp;
   my (@tojoin,$game,$pw);
   my ($a,$b,$c,$i);
   local $,;

   $ecopy=$jc;

   if ($jc =! /^\s*join\W+(\w+):(\w+):(.+)$/i) {
      $smtp = EnvelopeTo($to, 'Invalid "join" command');
   
      $smtp->datasend("\n  Invalid join command. Invalid command follows:\n\n $ecopy \n");

      $smtp->dataend();
      $smtp->quit;
      return;
   }

   $game=$1;
   $pw=$2;
   @tojoin = split (/:/,$3);

   $b=$UnShortName{lc($game)};
   if (!defined($b)) {
      $smtp = EnvelopeTo($to, 'Invalid "join" command');

      $smtp->datasend("\n  Invalid join command - Game $game not found.\n  Invalid command follows:\n\n $ecopy \n");

      $smtp->dataend();
      $smtp->quit;
      return;
   }

   if (uc($ini[$b]->val('General','Status')) eq 'INVITATION ONLY') {
      if ($main::cookie ne GenerateCookie($b, 'invite', $to) ) {
         $smtp = EnvelopeTo($to, 'Invalid "join" command');
   
         $smtp->datasend("\n  Invalid join command - Game $game is an invitation\nonly game, and you did not provide a valid cookie.\n\n  Invalid command follows:\n\n $ecopy \n");
   
         $smtp->dataend();
         $smtp->quit;
         return;
      }
   } elsif (uc($ini[$b]->val('General','Status')) ne 'OPEN') {
      $smtp = EnvelopeTo($to, 'Invalid "join" command');

      $smtp->datasend("\n  Invalid join command - Game $game is already running.\n  Invalid command follows:\n\n $ecopy \n");

      $smtp->dataend();
      $smtp->quit;
      return;
   }
   for (@tojoin) {
      $_=10 if /^a$/i;
      $_=11 if /^b$/i;
      $_ = int;
      if ($_ <1 || $_ > 11) {
         $smtp = EnvelopeTo($to, 'Invalid "join" command');
   
         $smtp->datasend("\n  Invalid join command - $_ is an invalid player number.\n  Invalid command follows:\n\n $ecopy \n");
   
         $smtp->dataend();
         $smtp->quit;
         return;
      }

      logit(5," Trying game $b, entry \"Player$_\"\n");
      #check to see if slot is empty.
      if (uc($ini[$b]->val("Player$_",'Active')) eq 'NO') {

         $ini[$b]->setval("Player$_",'Active','YES');
         $ini[$b]->setval("Player$_",'Password',$pw);
         $ini[$b]->setval("Player$_",'Email',$to);

         $smtp = EnvelopeTo($to, '"join" command successful');
   
         $smtp->datasend("\n  Join command successful - You have been added to \ngame $game as player $_");
         my @races = RaceNames($b);
         $smtp->datasend(", $races[$_-1]") if (@races);
         $smtp->datasend(".\n  Join command follows:\n\n $ecopy \n");
   
         $smtp->dataend();
         $smtp->quit;

         UpdateConfig($b);
         return;
      }
   }

   $smtp = EnvelopeTo($to, '"join" command unsuccessful');

   $smtp->datasend("\n  Join command unsuccessful - You have not been added to \ngame $game because all of your chosen races are already taken.\n  Join command follows:\n\n $ecopy \n");

   $smtp->dataend();
   $smtp->quit;
}

sub toadmin {

# Doesn't use EnvelopeTo because it needs a different 'From'.

   my $smtp = Net::SMTP->new($mainini->val('EMail','SMTP'));
   my $to;
   my $from = shift;
   my $auth = shift;
   my $authgame = shift;

   if (defined $auth && defined $authgame) {
      $to = $ini[$authgame]->val('Admin','Email');
      $to = $mainini->val('General','SysadminEmail') if (uc($to) eq 'NONE');
   }

   $to ||= $mainini->val('General','SysadminEmail');

   $smtp->mail($mainini->val('EMail','SMTPUser'));
   $smtp->to($to);
   $smtp->data();

   $smtp->datasend("From: $from\n");                          # fix?
   $smtp->datasend("To: VGA Plantes Administrator <$to>\n");
   $smtp->datasend('Subject: [VGA Planets] User Request'."\n");
   $smtp->datasend("\n");

   $smtp->datasend(shift) while (@_);

   $smtp->dataend();
   $smtp->quit;
}

sub broadcast {

# Doesn't use EnvelopeTo because it needs a different 'From'.
# Also sends multiple emails on one connection

   my $smtp = Net::SMTP->new($mainini->val('EMail','SMTP'));
   my $to;
   my $from;
   my ($a,$b);
   my $glob;
   my $auth = shift;
   my $authgame = shift;

   $a = shift while ($a !~ /^\s*broadcast\b/i);
   $glob .= ((shift)."\n") while (@_);   #Ha.

   if ($auth == -1) {     #sysadmin
      logit(4,"Sysadmin Broadcast\n");
      $from = "The VGAPd System Administrator";
      for ($b=1;$b<=$NumberOfGames;$b++) {
         for ($a=1;$a<=11;$a++) {
            if (uc($ini[$b]->val("Player$a",'Active')) ne 'NO') {
               $to = $ini[$b]->val("Player$a","Email");
      
               $smtp->mail($mainini->val('EMail','SMTPUser'));
               $smtp->to($to);
               $smtp->data();
            
               $smtp->datasend("From: $from\n");
               $smtp->datasend("To: $to\n");
               $smtp->datasend('Subject: [VGA Planets] Broadcast Message'."\n");
               $smtp->datasend("\n");
            
               $smtp->datasend($glob);
            
               $smtp->dataend();
            }
         }
      }
   } elsif (defined $authgame) {               #regular admin
      logit(4,"Admin of ".$ini[$authgame]->val("General","ShortName")." Broadcast\n");
      $from = "The Administrator of game ".$ini[$authgame]->val("General","ShortName")."<".$ini[$authgame]->val("Admin","Email").">";
      for ($a=1;$a<=11;$a++) {
         if (uc($ini[$authgame]->val("Player$a",'Active')) ne 'NO') {
            $to = $ini[$authgame]->val("Player$a",'Email');
   
            $smtp->mail($mainini->val('EMail','SMTPUser'));
            $smtp->to($to);
            $smtp->data();
         
            $smtp->datasend("From: $from\n");
            $smtp->datasend("To: $to\n");
            $smtp->datasend('Subject: [VGA Planets] Broadcast Message'."\n");
            $smtp->datasend("\n");
         
            $smtp->datasend($glob);
         
            $smtp->dataend();
         }
      }
   }

   $smtp->quit;
}

sub unsubmit {
   my $game = shift;
   my $player = shift;
   my $to = $ini[$game]->val("Player$player",'Email');
   my $turnnumber = TurnNumber($game);

   my $fndst;

   $fndst = "player$player.trn";

   unlink($ini[$game]->val('General','Homedir').$fndst) if (-e $ini[$game]->val('General','Homedir').$fndst);

   my $a;
   $a = $ini[$game]->val('General','ShortName');
   my $smtp = EnvelopeTo($to, "Unsubmit command success; turn $turnnumber of $a removed.\n");
      
   $smtp->datasend("  Your .TRN file has been deleted.\n");
   $smtp->datasend("  Everybody else is still waiting for your turn. Plese submit soon.\n");
   $smtp->datasend("\n  For your convenience, here is a check on game $a:\n\n");
   checkbody($smtp, $game);
   $smtp->dataend;
   $smtp->quit;
}

sub getTRN {
   my $game = shift;
   my $player = shift;
   my $nump = shift;
   my $entity = shift;
   my $to = $ini[$game]->val("Player$player",'Email');
   my ($a,$b,$ent,$io,$line);
   my $turnnumber = TurnNumber($game);
   my $phost = 0;

   my $missing = 128;
   my $stale = 1;
   my $truncated = 128;
   my $damaged = 2;
   my $wrongplayer = 128;
   my $badchecksum = 2;
   my $yellow = 128;
   my $red = 3;
   my $lastknown = 3;

   if ($ini[$game]->val('General','HostExecutable') =~ /phost/i) {
      $phost = 1;
      $missing = 1;
      $stale = 2;
      $truncated = 4;
      $damaged = 8;
      $wrongplayer = 16;
      $badchecksum = 32;
      $yellow = 64;
      $red = 128;
      $lastknown = 128;
   }

   my ($fndst, $fnold);

   $fndst = "player$player.trn";
   $fnold = $fndst.'.OLD';

   rename($ini[$game]->val('General','Homedir').$fndst, $ini[$game]->val('General','Homedir').$fnold) if (-e $ini[$game]->val('General','Homedir').$fndst);

   for ($a=1; $a<$nump; $a++) {
      $ent = $entity->parts($a);
      next if $ent->effective_type =~ /^text/;

      if ($io = $ent->open("r")) {
         open (FILE,'>'.$ini[$game]->val('General','Homedir').$fndst) or return;
         binmode FILE;
         while (defined ($line = $io->getline)) {
            print FILE $line;
         }
         $io->close;
         close FILE;
      }

      chomp($a = `pwd`);
      $b = $ini[$game]->val('General','VGAPdir');
      chdir($b);

      if ($phost) {
         logit(5,'Running phost -Q -c '."$player ".
                  $ini[$game]->val('General','Homedir')
                  .' from '.`pwd`."\n");
   
         $io = system('phost -Q -c '."$player ".
                      $ini[$game]->val('General','Homedir'));
         $io /= 256; # Or should this be outside the if?
                     # I seem to recall the old windows perl not needing
                     # the divide.
                     # ...but this is my memory.
      } else {
         $b=$player;
         $b='a' if ($player == 10);
         $b='b' if ($player == 11);
   
         logit(5,'Running chktrn '.
                  $ini[$game]->val('General','Homedir')." /$b"
                  .' from '.`pwd`."\n");
   
         $io = system('chktrn '.
                      $ini[$game]->val('General','Homedir')." /$b");
      }

      logit(5,"Changing to $a\n");
      chdir($a);

      logit(5,'Now back in '.`pwd`."\n");

      logit(4,"chktrn returned $io\n");

      if ($io == $missing || $io == $truncated || $io == $damaged
       || $io == $wrongplayer || $io == $badchecksum || $io > $lastknown) {

         my $smtp = EnvelopeTo($to, 'Submit command error!');
      
         $a = $ini[$game]->val('General','ShortName');
#         $smtp->datasend("To: \"Player $player");
#         @races = RaceNames($game);
#         $smtp->datasend(", $races[$player-1],") if (defined @races);
#         $smtp->datasend(" of game $a\" <$to>\n");

         $smtp->datasend("  Error: CHKTRN reports that your .TRN file was corrupted.\n");
         $smtp->datasend("         Please try re-submitting turn $turnnumber of game $a.\n");
         $smtp->dataend;
         $smtp->quit;

         unlink($ini[$game]->val('General','Homedir').$fndst);
         if (-e $ini[$game]->val('General','Homedir').$fnold) {
            rename($ini[$game]->val('General','Homedir').$fnold,$ini[$game]->val('General','Homedir').$fndst);
         }
      } elsif ($io == $stale) {
         my $smtp = EnvelopeTo($to, 'Submit command error!');
      
         $a = $ini[$game]->val('General','ShortName');
#         $smtp->datasend("To: \"Player $player");
#         @races = RaceNames($game);
#         $smtp->datasend(", $races[$player-1],") if (defined @races);
#         $smtp->datasend(" of game $a\" <$to>\n");
      
         $smtp->datasend("  Error: CHKTRN reports that your .TRN file was stale.\n");
         $smtp->datasend("         Please try submitting the correct, most recent, turn.\n");
         $smtp->datasend("         This game is $a; The current turn number is $turnnumber.\n");

         $smtp->dataend;
         $smtp->quit;

         unlink($ini[$game]->val('General','Homedir').$fndst);
         if (-e $ini[$game]->val('General','Homedir').$fnold) {
            rename($ini[$game]->val('General','Homedir').$fnold,$ini[$game]->val('General','Homedir').$fndst);
         }
      } else {

         unlink ($fnold) if (-e $fnold);
   
         $a = $ini[$game]->val('General','ShortName');
         my $smtp = EnvelopeTo($to, "Submit command success; turn $turnnumber of $a approved.\n");
      
#         $smtp->datasend("To: \"Player $player");
#         @races = RaceNames($game);
#         $smtp->datasend(", $races[$player-1],") if (defined @races);
#         $smtp->datasend(" of game $a\" <$to>\n");
      
         $smtp->datasend("  Your .TRN file has been accepted for processing.\n");
         $smtp->datasend("  You will be notified when the turn is run. Thank You.\n");
         $smtp->datasend("\n  For your convenience, here is a check on game $a:\n\n");
         checkbody($smtp, $game);
         $smtp->dataend;
         $smtp->quit;

         if ($io == $red || $io == $yellow) {
            $to = $ini[$game]->val('Admin','Email');
            my $subject;
            if ($io == $yellow) {
               $subject="Player $player is acting weird.\n";
            } else {
               $subject="Player $player is attempting to cheat.\n";
            }
            my $smtp = EnvelopeTo($to, $subject);
         
            $a = $ini[$game]->val('General','ShortName');
#            $smtp->datasend("To: Administrator of game $a <$to>\n");
         
            $smtp->datasend("  According to CHKTRN, player $player");
            my @races = RaceNames($game);
            $smtp->datasend(", $races[$player-1],") if (@races);
            if ($io == $yellow) {
               $smtp->datasend(" may\n be attempting to cheat, but probably not.\n This is in");
            } else {
               $smtp->datasend(" is\nattempting to cheat in");
            }
            $smtp->datasend(" game $a, on turn $turnnumber.\n");
            $smtp->datasend("Feel free to react as you see fit.\n");
            $smtp->dataend;
            $smtp->quit;
         }
         unlink ($ini[$game]->val('General','Homedir').$fnold) if (-e $ini[$game]->val('General','Homedir').$fnold);
      }

      return;
   }

   my $smtp = EnvelopeTo($to, 'Submit command error!');

   $a = $ini[$game]->val('General','ShortName');
#   $smtp->datasend("To: \"Player $player of game $a\" <$to>\n");

   $smtp->datasend("  Error: No binary attachment found in email.\n");
   $smtp->datasend("         Please try attaching a turn file next time.\n");
   $smtp->datasend("\n         (in case you were wondering, I was expecting\n          turn $turnnumber of $a)\n");
   $smtp->dataend;
   $smtp->quit;

   rename($ini[$game]->val('General','Homedir').$fnold,$ini[$game]->val('General','Homedir').$fndst) if (-e $ini[$game]->val('General','Homedir').$fnold);
}

sub sendRST {

# Doesn't use EnvelopeTo because we need MIME

   my $game = shift;
   my $player = shift;
   my $to = $ini[$game]->val("Player$player",'Email');
   my $a;
   my $gamename;
   my $turnnumber = TurnNumber($game);

   my $glob;
   local $/;

   $a = $ini[$game]->val('General','Homedir') . "player$player.rst";

   if (!open(FILE,$a)) {
      # Presume sysadmin ran killrace, or PAAGhoul got 'em or something.
      logit(1, "The RST of Player $player won't open. Presuming dead.\n");
      $ini[$game]->setval("Player$player",'Active','NO');
      UpdateConfig($game);
      return;
   }
   binmode FILE;
   $glob = <FILE>;
   close FILE;

   my $smtp = Net::SMTP->new($mainini->val('EMail','SMTP'));

   $smtp->mail($mainini->val('EMail','SMTPUser'));
   $smtp->to($to);
   $smtp->data();

   $gamename = $ini[$game]->val('General','ShortName');

   my $from = $mainini->val('General','VGAPEmail');
   $smtp->datasend("From: \"Pete\'s Automated VGAP Thingy\" <$from>\n");
   $smtp->datasend("To: \"Player $player of game $gamename\" <$to>\n");
   $smtp->datasend('Subject: [VGA Planets] RST file for '."turn $turnnumber of $gamename.\n");
   $smtp->datasend('MIME-Version: 1.0'."\n");
   $smtp->datasend('Content-Type: multipart/mixed; boundary="troZ"'."\n");
   $smtp->datasend("\n");
   $smtp->datasend("This is a multi-part message in MIME format.\n");

   $a = $ini[$game]->val('General','Homedir') . "util$player.dat";

   $smtp->datasend("\n--troZ\n");
   $smtp->datasend("Content-Type: text/plain\n");
#   $smtp->datasend("Content-Disposition: inline\n");
   $smtp->datasend("Content-Transfer-Encoding: 7bit\n");
   $smtp->datasend("\n");
   if (-e $a) {
      $smtp->datasend("Here are your files for turn $turnnumber of $gamename\n");
   } else {
      $smtp->datasend("Here is your file for turn $turnnumber of $gamename\n");
   }

   $smtp->datasend("\n--troZ\n");
   $smtp->datasend('Content-Type: application/octet-stream; name="'."PLAYER$player.RST"."\"\n");
   $smtp->datasend('Content-Transfer-Encoding: base64'."\n");
   $smtp->datasend('Content-Disposition: attachment; filename="'."PLAYER$player.RST"."\"\n");
   $smtp->datasend("\n");

   $smtp->datasend(encode_base64($glob));

   if (open(FILE,$a) ) {
     binmode FILE;
     $glob = <FILE>;
     close FILE;

     $smtp->datasend("\n--troZ\n");
     $smtp->datasend('Content-Type: application/octet-stream; name="'."UTIL$player.DAT\"\n");
     $smtp->datasend('Content-Transfer-Encoding: base64'."\n");
     $smtp->datasend('Content-Disposition: attachment; filename="'."UTIL$player.DAT\"\n");
     $smtp->datasend("\n");

     $smtp->datasend(encode_base64($glob));
   }

   $a = $ini[$game]->val('General','Homedir') . "xyplan$player.dat";
   if (open(FILE,$a) ) {
     binmode FILE;
     $glob = <FILE>;
     close FILE;

     $smtp->datasend("\n--troZ\n");
     $smtp->datasend('Content-Type: application/octet-stream; name="'."XYPLAN.DAT\"\n");
     $smtp->datasend('Content-Transfer-Encoding: base64'."\n");
     $smtp->datasend('Content-Disposition: attachment; filename="'."XYPLAN.DAT\"\n");
     $smtp->datasend("\n");

     $smtp->datasend(encode_base64($glob));
   }

   $smtp->datasend("\n--troZ--\n");
   $smtp->dataend;
   $smtp->quit;

   logit(4,"player$player.rst sent!\n");
}

sub warnplayer {
   my $game = shift;
   my $player = shift;
   my $to = $ini[$game]->val("Player$player",'Email');
   my $a;

   my $gamename = $ini[$game]->val('General','ShortName');
   my $smtp = EnvelopeTo($to, "Warning about $gamename!");

   logit(4,"Warning $to about $gamename\n");

#   $smtp->datasend("To: \"Player $player of game $a\" <$to>\n");

   $a = $ini[$game]->val('Running','Warning');
   $smtp->datasend("  Warning! The host will be running game $gamename in $a, and\nwe don't have your .TRN file yet.\n");

   $smtp->dataend();
   $smtp->quit;
}

sub warngame {
   my $game = shift;
   my $b;

   $ini[$game]->setval('NextRun','NextWarn','Never');
   UpdateConfig($game);

   for ($b=1; $b<=11; $b++) {
      my $fn;

      if (uc($ini[$game]->val("Player$b",'Active')) eq 'YES') {
         $fn = $ini[$game]->val('General','Homedir');
         $fn .= "player$b.trn";
         if (! (-e $fn)) {
            warnplayer($game,$b);
         }
      }
   }
}

sub NextRunTime {
   my $next = shift;
   my $game = shift;
   my $now = ParseDate('today');

   $next = DateCalc($next,'+'.$ini[$game]->val('Running','Frequency'));
   my @skip = split(/:/,$ini[$game]->val('Running','Exempt'));
   my $a = 1;
   while ($a) {
      $a = 0;
      foreach my $skip (@skip) {
         if ($skip) {
            my $b = Date_GetNext($now,$skip);
            if ($b le $next) {
               $next = DateCalc($next,'+ 24 hours');
               $a = 1;
               $skip = 0;
            }
         }
      }
   }

   return $next;
}

sub rungame {
   my $game = shift;
   my $gamename = $ini[$game]->val('General','ShortName');
   my $now = ParseDate('today');
   my $cmdline;
   my $compline;
   my $compon = 1;
   my ($a,$b,$next,$skip);
   my @skip;
   my $cwd;

   $cmdline = $ini[$game]->val('General','HostExecutable');
   $cmdline .= ' '.$ini[$game]->val('General','Homedir');

   $compline = $ini[$game]->val('General','ComputerPlayer');
   $compon = 0 if (length($compline) < 1);
   $compline .= ' '.$ini[$game]->val('General','Homedir');
   $compline .= ' /';
   undef $b;
   for ($a=1;$a<=11; $a++) {
      if (uc($ini[$game]->val("Player$a",'Active')) eq 'COMP' and !(-e $ini[$game]->val('General','Homedir')."player$a.trn")) {
         $b = $a;
         $b = 'a' if ($b == 10);
         $b = 'b' if ($b == 11);
         $compline .= "$b";
      }
   }
# Uncomment the next line for unregistered computer players
#   $compline .= '+';

   chomp($cwd = `pwd`);
   chdir($ini[$game]->val('General','VGAPdir'));   #sigh.

   `$compline` if (defined($b) && $compon);
   logit(2,"Host running for $gamename.\n");
   `$cmdline`;

   chdir($cwd);

   $next = ( (shift)?$now:$ini[$game]->val('NextRun','NextRun') );
   $ini[$game]->setval('NextRun','LastRun',$next);

   $next = NextRunTime($next, $game);

   $ini[$game]->setval('NextRun','NextRun',$next);
   $ini[$game]->setval('NextRun','NextWarn',DateCalc($next,'-'.$ini[$game]->val('Running','Warning') ));

   UpdateConfig($game);

   my $sn = $ini[$game]->val('General','ShortName');
   chdir($mainini->val('Backup','Directory'));

   $cmdline = 'zip -9 -X -j ';
   if (uc($mainini->val('Backup','Style')) eq 'ROTATE') {
      unlink($sn.'5.zip');
      for ($b=4;$b>=1;$b--) {
         rename $sn.$b.'.zip',
                $sn.($b+1).'.zip';
      }
      rename $sn.'.zip',
             $sn.'1.zip';
      $cmdline .= $sn;
   } else {
      mkdir $sn unless (-d $sn);
      chdir $sn;
      $cmdline .= 'turn'.TurnNumber($game);
   }
   $cmdline .= ' '.$ini[$game]->val('General','Homedir');
   $cmdline .= '*';
   `$cmdline`;

   chdir($cwd);

   for ($b=1; $b<=11; $b++) {
      sendRST($game,$b) if (uc($ini[$game]->val("Player$b",'Active')) ne 'NO')
   }
}

sub pausegame {
   my $game = shift;
   my $gamename = $ini[$game]->val('General','ShortName');

   $ini[$game]->setval('General','Status','Paused');
   $ini[$game]->setval('NextRun','NextRun','Never');
   $ini[$game]->setval('NextRun','NextWarn','Never');
   UpdateConfig($game);

   logit(2,"$gamename is paused.\n");
   my $smtp = EnvelopeTo($ini[$game]->val('Admin','Email'), "Pausing $gamename");
   $smtp->datasend("$gamename is now paused.\n");

   $smtp->dataend();
   $smtp->quit;
}

sub IniLoad {
   my $a;

   -e $ininame or die ("Can't open $ininame.\n");

   $mainini = new IniConf( -file => $ininame );

   $maininitime=(stat($ininame))[9];
   die ("Can't open $ininame.\n") if (!defined($mainini)); # Stupidity check. Enjoy it. There aren't too many of these.

# Keep or remove? Hmmmmm...
   $LogFileName= $mainini->val('General','LogFileName');
   $DebugLog= $mainini->val('General','LogFileDebugLevel');
   $DebugCon= $mainini->val('General','ConsoleDebugLevel');

   $POPhost= $mainini->val('EMail','POP');
   $POPuser= $mainini->val('EMail','POPUser');
   $POPpw= $mainini->val('EMail','POPpw');

   $MainDelay= $mainini->val('General','CheckEvery');

   die("Error: Must supply delay value!\n") if (!defined $MainDelay);

   $NumberOfGames= $mainini->val('General','NumberOfGames');

   for ($a=1; $a<=$NumberOfGames; $a++) {

      my $gameini= $mainini->val('GameINI',"Game$a");
      $initime[$a]=(stat($gameini))[9];
      $ini[$a]=new IniConf( -file => $gameini );
      $UnShortName{lc($ini[$a]->val('General','ShortName'))}=$a;

   }
}

$ininame = 'vgapd.ini' if (!defined($ininame=shift));

my $parser = new MIME::Parser;
$parser->output_to_core('ALL'); #For now.

$running = 1;
$maininitime = -1; #supress warning about " != on undefined" below

$SIG{'INT'} = sub { $running = 0 };
$SIG{'HUP'} = sub { $running = 0 };

VGAPd: while (1) {
   my ($a, $i);
   my $nonVGAP;
   my $pop;

   $nonVGAP = 0;

   IniLoad if ($maininitime != (stat($ininame))[9]);
   for ($a=1; $a<=$NumberOfGames; $a++) {
      IniLoad if ($initime[$a] != (stat( $mainini->val('GameINI',"Game$a") ))[9]);
   }

   undef $pop;

   logit(6," - Mail hammering loop\n");
   while (!defined($pop))  { $pop = Net::IMAP::Simple->new($POPhost) or sleep(5); }
   logit(6," - Mail login\n");
   $pop->login($POPuser,$POPpw);
#   @pstat=$pop->popstat();
#   $messageCount = $pstat[0];
   my $messageCount = $pop->select('Inbox');
   logit(5,"Checking Mail server. Number of messages: $messageCount\n");
   for ($i=1;$i<=$messageCount;$i++) {
      my $topr=$pop->top($i,1);
      for (@$topr) {
         if (/^Subject:/i) {
            logit(6,$_);
            if(/VGA ?P/i) {  #must be case insensitive, because Outlook Express is.

               # I'd love to do a fork here, but Perl doesn't support fork
               # under windoze (yet).
               # 5.005 is rumoured to support threads [ie. async { } ]
               # but it's not out yet (and I don't know if they will work
               # on 95-NT).

               # Revision: 5.005 is out now. But I'm too lazy to install it.

               my $commandsfound=0;
               local $main::cookie=undef;
               my $auth = undef;
               my $authgame = undef;

               if ($pop->list($i) > 256000) { #ignore messages over approx 256K
                  $pop->delete($i);
                  next;
               }

               my $msgr=$pop->get($i);

               logit(6,@$msgr,"\n");
   
               my $entity = $parser->parse_data(\@$msgr);
               my $ent;
               my $type = $entity->effective_type;
               my $nump = $entity->parts;
               my $head=$entity->head;
               logit(8,$head->as_string,"\n");
               logit(8,$entity->as_string,"\n");
               my $from=$head->get('from');
               chomp($from);
               logit(7,$from,"\n");
               $from = $1 if ($from =~ /<(.+)>/);
               $from =~ s/\(.*?\)//g;
               logit (6,"Number of parts: $nump   Type: $type\n");
               if ($type =~ /^multipart/) {
                  $ent = $entity->parts(0);
               } else {
                  $ent=$entity;
               }
               $ent = $ent->parts(0) while $ent->effective_type =~ /^multipart/;
               $type = $ent->effective_type;

               $pop->delete($i);  #move this up here... delete even if we can't find a 'text/*' section.

               last if $type !~ /^text/;
   
               my $body = $ent->bodyhandle;
               my $text = $body->as_string;
               my @text = split(/\n/,$text);

               for (@text) {
                  chomp;
                  logit(6," - ",$_,"\n");
                  if (/^\s*help\b/i) {
                     $commandsfound++;
                     helpto($from) if ($from);
                  }
                  if (/^\s*list\b/i) {
                     $commandsfound++;
                     listto($from) if ($from);
                  }
                  if (/^\s*desc\s+(\w+)\s*$/i) {
                     $commandsfound++;
                     descto($from, $UnShortName{lc($1)}, $auth, $authgame) if ($from);
                  }
                  if (/^\s*check\s+(\w+)\s*$/i) {
                     $commandsfound++;
                     checkto($from,$1) if ($from);
                  }
                  if (/^\s*shutdown\b/i) {
                     if ($auth == -1) {
                        $commandsfound++;
                        $pop->quit();
                        last VGAPd;
                     }
                  }

                  if (/^\s*resend\b/i) {
                     $commandsfound++;
                     sendRST($authgame,$auth) if (defined ($auth) and $auth > 0);
                  }
                  if (/^\s*email (.*\@.*)$/i) {
                     $commandsfound++;
                     logit (5,"Email command found ($_).\n");
                     if (defined ($auth) and $auth > 0) {
                        logit (4,"Changing e-mail of player $auth of game $authgame to $1.\n");
                        $ini[$authgame]->setval("Player$auth",'Email',$1);
                        UpdateConfig($authgame);
                     } else {
                        invalidto($from,$_) if ($from);
                     }
                  }
                  if (/^\s*pass\s+(.+)\s*$/i) {
                     $commandsfound++;
                     logit (5,"Email command found ($_).\n");
                     if (defined ($auth) and $auth > 0) {
                        logit (3,"Changing password of player $auth of game $authgame to $1.\n");
                        $ini[$authgame]->setval("Player$auth",'Password',$1);
                        UpdateConfig($authgame);
                     } else {
                        invalidto($from,$_) if ($from);
                     }
                  }
                  if (/^\s*resign\b/i) {
                     $commandsfound++;
                     logit (5,"Resign command found.\n");
                     if (defined ($auth) and $auth > 0) {
                        logit (3,"Player $auth of game $authgame is resigning.\n");
                        resign($auth, $authgame);
                     } else {
                        invalidto($from,$_) if ($from);
                     }
                  }
                  if (/^\s*kill\s+(\d+)\s*$/i) {
                     $commandsfound++;
                     my $kill = $1;
                     logit (5,"Kill command found.\n");
                     if (defined ($auth) and $auth == 0) {
                        logit (3,"Admin killing Player $kill of game $authgame.\n");
                        killrace($kill, $authgame);
                     } else {
                        invalidto($from,$_) if ($from);
                     }
                  }
                  if (/^\s*submit\b/i) {
                     $commandsfound++;
# ignored now. Implied by attachment.                     getTRN($authgame,$auth) if (defined ($auth) and $auth > 0);
                  }
                  if (/^\s*unsubmit\b/i) {
                     $commandsfound++;
                     unsubmit($authgame,$auth) if (defined ($auth) and $auth > 0);
                  }
                  if (/^\s*end\b/i) {
                     $commandsfound++;
                     last;
                  }
                  if (/^\s*comp\b/i) {
                     $commandsfound++;
                     if (defined $auth and $auth > 0) {
                        if (/on/i or /yes/i) {
                           $ini[$authgame]->setval("Player$auth",'Active','COMP');
                        } else {
                           $ini[$authgame]->setval("Player$auth",'Active','YES');
                        }
                        UpdateConfig($authgame);
                     }
                  }
                  if (/^\s*pause\b/i) {
                     $commandsfound++;
                     if (defined $auth && $auth == 0 && uc($ini[$authgame]->val('General','Status')) eq 'RUNNING') {
                        pausegame($authgame);
                     } else {
                        invalidto($from,$_) if ($from);
                     }
                  }
                  if (/^\s*resume\b/i) {
                     $commandsfound++;
                     if (defined $auth && $auth == 0 && uc($ini[$authgame]->val('General','Status')) eq 'PAUSED') {
                        my $next = ParseDate('today');

                        $next = NextRunTime($next, $authgame);

                        $ini[$authgame]->setval('General','Status','Running');
                        $ini[$authgame]->setval('NextRun','NextRun',$next);
                        $ini[$authgame]->setval('NextRun','NextWarn',DateCalc($next,'-'.$ini[$authgame]->val('Running','Warning') ));
                        UpdateConfig($authgame);
                     } else {
                        invalidto($from,$_) if ($from);
                     }
                  }
                  if (/^\s*start\b/i) {
                     my $status;
                     $commandsfound++;
                     defined $authgame and $status = $ini[$authgame]->val('General','Status');
                     if (defined $auth && $auth == 0 && (uc($status) eq 'OPEN' || uc($status) == 'INVITATION ONLY')) {
                        $ini[$authgame]->setval('General','Status','Running');
                        $ini[$authgame]->setval('NextRun','NextRun',ParseDate('today'));
                        UpdateConfig($authgame);
                     } else {
                        invalidto($from,$_) if ($from);
                     }
                  }
                  if (/^\s*admin\b/i) {
                     $commandsfound++;

                     toadmin($from, $auth, $authgame, @$msgr);
                     last;
                  }

                  if (/^\s*cookie\s+(\S+)\s*$/i) {
                     $commandsfound++;
                     $main::cookie = $1;
                  }
                  if (/^\s*invite\s+(\S+)\s*$/i) {
                     my $to = $1;
                     $commandsfound++;
                     if (defined $auth && $auth == 0) {
                        invite($to,$authgame);
                     } else {
                        invalidto($from, $_) if ($from);
                     }
                  }

                  if (/^\s*auth\b/i) {
                     my ($game, $user, $pass);

                     /^\s*auth\s+([\w:]+)/i;
                     logit(5,"Parsing auth\n");
                     ($game,$user,$pass)=split /:/,$1;
                     logit(4,"Game: $game; User: $user; Pass: $pass\n");

                     $commandsfound++;

                     if ($user eq 'sysadmin') {
                        logit(2,"$from attempting sysadmin login\n");
                        if ($pass eq $mainini->val('General','SysadminPW')) {
                           $authgame=$UnShortName{lc($game)};
                           $auth = -1;                #Is this what I want?
                        } else {
                           logit(0,"$from: UNSUCCESSFUL SYSADMIN LOGIN ATTEMPT!\n");
                           undef $auth;
                           authinvalidto($from,$_) if ($from);
                        }
                     } elsif ($user eq 'admin') {
                        $authgame=$UnShortName{lc($game)};
                        logit(3,"$from attempting admin login to game $game\n");
                        if (defined $authgame) {
                           if ($pass eq $ini[$authgame]->val('Admin','Password')) {
                              $auth=0;
                           } else {
                              logit(1,"$from: UNSUCCESSFUL ADMIN LOGIN ATTEMPT! :$game\n");
                              undef $auth;
                              authinvalidto($from,$_) if ($from);
                           }
                        } else {
                           undef $auth;
                           authinvalidto($from,$_) if ($from);
                        }
                     } else {
                        $user =~ s/^a$/10/i;
                        $user =~ s/^b$/11/i;
                        $authgame=$UnShortName{lc($game)};
                        if (defined $authgame &&
                              $user>0 && $user<12 &&
                              uc($ini[$authgame]->val("Player$user",'Active')) ne 'NO') {
                           if ($pass eq $ini[$authgame]->val("Player$user",'Password')) {
                              $auth=$user;
                           } else {
                              undef $auth;
                              authinvalidto($from,$_) if ($from);
                              logit(3,"$from: Invalid Password. :$game:$user\n");
                           }
                        } else {
                           undef $auth;
                           authinvalidto($from,$_) if ($from);
                        }
                     }
                     logit(4,"Auth: $auth; AuthGame: $authgame\n");
                  } #end Auth

                  if (/^\s*join\b/i) {
                     $commandsfound++;
                     joingame($from,$_) if ($from);
                  }

                  if (/^\s*broadcast\b/i) {
                     if (defined $auth) {
                        if ($auth < 1) {
                           $commandsfound++;

                           broadcast($auth, $authgame, @text);
                        }
                     }
                     last;
                  }

               }
   
               #while (shift @$msgr ne "\n") {};
               #print @$msgr;
               getTRN($authgame,$auth, $nump, $entity) if ( ($nump > 1) && defined ($auth) and $auth > 0);

               if (!$commandsfound) {
                  $head=$ent->head;
                  $from=$head->get('from');
                  chomp($from);
                  $from = $1 if ($from =~ /<(.*)>/);
                  $from =~ s/\(.*?\)//g;
                  helpto($from,1) if ($from);
               }

               last;
            }
            else { $nonVGAP++; }
         }
      }
   }
   $pop->quit();

# Check for active games that can be run here.

   for ($a=1; $a<=$NumberOfGames; $a++) {
      if (uc($ini[$a]->val('NextRun','NextRun')) ne 'NEVER' && uc($ini[$a]->val('General','Status')) eq 'RUNNING') {
         if (ParseDate($ini[$a]->val('NextRun','NextRun')) le ParseDate('today')) {
            rungame($a,0);
         } elsif (uc($ini[$a]->val('Running','RunEarly')) eq 'YES') {
            my $run = 1;
            my $b;
            for ($b=1; $b<=11; $b++) {
               my $fn;

               if (uc($ini[$a]->val("Player$b",'Active')) eq 'YES') {
                  $fn = $ini[$a]->val('General','Homedir');
                  $fn .= "player$b.trn";
                  if (! (-e $fn)) {
                     $run = 0;
                     last;
                  }
               }
            }
            rungame($a,1) if ($run);
         }
      }
      if (uc($ini[$a]->val('NextRun','NextWarn')) ne 'NEVER') {
         if (ParseDate($ini[$a]->val('NextRun','NextWarn')) le ParseDate('today')) {
            warngame($a);
         }
      }
   }

   last VGAPd if (!$running);
   sleep($MainDelay);
   last VGAPd if (!$running);
}

logit(1,"Clean Shutdown\n");
